#include "StdAfx.h"
#include ".\drawablewidget.h"

#include <gl/gl.h>
#include <gl/glu.h>
#include <gl/glut.h>
#include <math.h>

#include "glMatrixScope.h"

#include "polynomial.h"

polynomial<3, double> testPoly;

BEGIN_MESSAGE_MAP(CDrawableWidget, CWnd)
	ON_WM_CREATE()
	ON_WM_SYSCOMMAND()
	ON_WM_PAINT()
	ON_WM_QUERYDRAGICON()
	ON_WM_MOUSEMOVE()
	//}}AFX_MSG_MAP
END_MESSAGE_MAP()



CDrawableWidget::CDrawableWidget() 
{ 
	m_bRayPickerValid = false;
	m_pViewInfo = NULL;

	m_lfNear = 3.0; 
	m_lfFar = 1000.0;

	m_pCDC = NULL;
	m_pLinkedWidget = NULL;
	m_bPixelsSetUp = false;
	m_pointMouseAsInts.x = 
		m_pointMouseAsInts.y = 0;
	m_arrLfMousePos[0] = m_arrLfMousePos[1] = 0;
	m_bOglInitialized=false;
	m_bMouseStarted=false;
	m_pCustomOnMouse = NULL;
	m_bPerspective = false;
}

static const double g_lfNastyPersepctiveHack = 45.0;

GLvoid initializeGL(GLsizei width, GLsizei height, bool bPerspective, double lfNear, double lfFar) 
{ 
    GLfloat     aspect; 
    GLdouble    near_plane, far_plane; 


 
    // glClearIndex( (GLfloat)BLACK_INDEX); 
	glClearColor((GLclampf)0.0, (GLclampf)0.0, (GLclampf)0.0, (GLclampf)0.0);
    // glClearDepth( 1000.0 ); 
  
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LEQUAL);
	glEnable(GL_COLOR_MATERIAL);
 
    glMatrixMode( GL_PROJECTION );
	// glLoadIdentity();
    aspect = (GLfloat) width / height; 

	
	
    // gluPerspective( 45.0, aspect, 3.0, 7.0 ); 
	// glOrtho(-100.0f, windowWidth, -100.0f, windowHeight, -200.0f, 200.0f);
	if(bPerspective)
	{
		gluPerspective( g_lfNastyPersepctiveHack, aspect, lfNear, lfFar ); 
	}
	else
	{
		glOrtho(-100.0*aspect, 100.0*aspect, -100.0, 100.0, -lfNear, lfFar);
	}
    glMatrixMode( GL_MODELVIEW ); 
 
    near_plane = 10.0; //3.0; 
    far_plane = 1000.0; //7.0; 
 
	gluLookAt(0.0, 0.0, 150.0, // eye
		0.0, 0.0, 0.0, // center
		0.0, 1.0, 0.0); // up
    //latitude = 0.0F; 
    //longitude = 0.0F; 
    //latinc = 6.0F; 
    //longinc = 2.5F; 
 
    // createObjects(); 

	
}

CDC * CDrawableWidget::SafeGrabDC()
{
	if(!m_pCDC)
	{
		m_pCDC = GetDC();
	}
	return m_pCDC;
}

void CDrawableWidget::SafeReleaseDC()
{
	if(m_pCDC)
	{
		ReleaseDC(m_pCDC);
		m_pCDC = NULL;
	}
}

BOOL CDrawableWidget::SetupPixelFormat() 
{ 
	// static bool m_bPixelsSetUp = false;

	if(m_bPixelsSetUp)
	{
		return TRUE;
	}
	 
    PIXELFORMATDESCRIPTOR pfd, *ppfd; 
    int pixelformat; 
 
    ppfd = &pfd; 
 
    ppfd->nSize = sizeof(PIXELFORMATDESCRIPTOR); 
    ppfd->nVersion = 1; 
    ppfd->dwFlags = PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL |  
                        PFD_DOUBLEBUFFER; 
    ppfd->dwLayerMask = PFD_MAIN_PLANE;  // PFD_OVERLAY_PLANE
    ppfd->iPixelType = PFD_TYPE_RGBA; 
    ppfd->cColorBits = 32; 
    ppfd->cDepthBits = 32; 
    ppfd->cAccumBits = 0; 
    ppfd->cStencilBits = 0; 

	PIXELFORMATDESCRIPTOR test_pfd = { 
		sizeof(PIXELFORMATDESCRIPTOR),   // size of this pfd 
		1,                     // version number 
		PFD_DRAW_TO_WINDOW |   // support window 
		PFD_SUPPORT_OPENGL |   // support OpenGL 
		PFD_DOUBLEBUFFER,      // double buffered 
		PFD_TYPE_RGBA,         // RGBA type 
		32,                    // 24-bit color depth 
		0, 0, 0, 0, 0, 0,      // color bits ignored 
		0,                     // no alpha buffer 
		0,                     // shift bit ignored 
		0,                     // no accumulation buffer 
		0, 0, 0, 0,            // accum bits ignored 
		32,                    // 32-bit z-buffer 
		0,                     // no stencil buffer 
		0,                     // no auxiliary buffer 
		PFD_MAIN_PLANE,        // main layer PFD_OVERLAY_PLANE, //
		0,                     // reserved 
		0, 0, 0                // layer masks ignored 
	};

	ppfd = &test_pfd;
	// figure out how to actually do this later

	CDC * pCDC = SafeGrabDC();

	if(!pCDC)
	{
		return FALSE;
	}

	HDC hdcTest = pCDC->GetSafeHdc();
	
    pixelformat = ChoosePixelFormat(pCDC->GetSafeHdc(), ppfd); 

	DWORD pixError = GetLastError();

	DWORD pixGetError = 0;
	if(!pixelformat)
	{
		pixelformat = ::GetPixelFormat(pCDC->GetSafeHdc());
		pixGetError = GetLastError();
	}
 
    if ( (pixelformat = ChoosePixelFormat(SafeGrabDC()->GetSafeHdc(), ppfd)) == 0 ) 
    { 
        TRACE("ChoosePixelFormat failed %d\n", pixGetError);// "Error", MB_OK); 
		
        return FALSE; 
    } 
 
    if (SetPixelFormat(SafeGrabDC()->GetSafeHdc(), pixelformat, ppfd) == FALSE) 
    { 
        TRACE("SetPixelFormat failed %d\n", "Error", pixGetError);// "Error", MB_OK); 
		
        return FALSE; 
    }
 
	// MessageBox("Pixel Format stuff succeeded");

	m_bPixelsSetUp = true;

    return TRUE; 
} 

HGLRC CDrawableWidget::InitOglStuff()
{
	/*if(!m_bOglInitialized)
	{*/
	// TRACE("Initialized OGL stuff\n");
	// m_ghDC = GetDC(); 
    if (!SetupPixelFormat()) 
	{
		// return;
        // PostQuitMessage (0); 
	}
	
	
    HGLRC ghRC = wglCreateContext(SafeGrabDC()->GetSafeHdc()); 
	if(ghRC != NULL)
	{
		m_bOglInitialized = true;
	}
	else
	{
		DWORD nCurrError = GetLastError();

		LPVOID lpMsgBuf;

		FormatMessage(
			FORMAT_MESSAGE_ALLOCATE_BUFFER | 
			FORMAT_MESSAGE_FROM_SYSTEM,
			NULL,
			nCurrError,
			MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT),
			(LPTSTR) &lpMsgBuf,
			0, NULL );

		TRACE("CreateContext failed %d (\"%s\")\n", nCurrError, lpMsgBuf);
	}
    wglMakeCurrent(SafeGrabDC()->GetSafeHdc(), ghRC); 
    GetClientRect(&m_rect); 
	// TRACE("%d %d -- %d %d\n", m_rect.right, m_rect.top, m_rect.left, m_rect.bottom);
    initializeGL(m_rect.right, m_rect.bottom, TRUE, m_lfNear, m_lfFar);
	m_lfAspect = m_rect.right/(double)m_rect.bottom;

	if(m_bRayPickerValid)
	{
		SetRayPickerVals(m_rayPicker);
	}
	return ghRC;
}

bool CDrawableWidget::SetRayPickerVals(ScreenRayPicker & SetMe)
{
	if(!m_bPerspective || !m_bOglInitialized)
	{
		return false;
	}

	SetMe.SetAspect(m_lfAspect);
	SetMe.SetAngle(GenericMath::pi*g_lfNastyPersepctiveHack/180.0);
	// SetMe.SetView(
	SetMe.SetNearClip(m_lfNear);
	if(!m_pViewInfo)
	{
		return false;
	}
	SetMe.SetView(*m_pViewInfo);
	return true;
}

CImage * CDrawableWidget::GetBlankImage(double lfSizeScale, bool bHasAlpha)
{
	CImage * pResult = NULL;
	pResult = new CImage;

	GetClientRect(&m_rect);
	int nWidth = static_cast<int>(lfSizeScale*m_rect.right);
	int nHeight = static_cast<int>(lfSizeScale*m_rect.bottom);
	
	
	pResult->Create(nWidth, nHeight, 24+((!!bHasAlpha)<<3));

	return pResult;
}

bool CDrawableWidget::GrabScreen(CImage & imgGrabTo)
{
	
	GetClientRect(&m_rect); 
	
	StretchBlt(imgGrabTo.GetDC(), 0, 0, imgGrabTo.GetWidth(), imgGrabTo.GetHeight(), 
		SafeGrabDC()->GetSafeHdc(), 0, 0, m_rect.right, m_rect.bottom, SRCCOPY);

	imgGrabTo.ReleaseDC();
	SafeReleaseDC();
	return true;

	//GetClientRect(&m_rect); 
	//HBITMAP hBmpResult = CreateCompatibleBitmap(GetDC()->GetSafeHdc(),
	//	m_rect.right,m_rect.bottom);


	//return hBmpResult;
}

bool CDrawableWidget::SaveScreen(const CString & strFileName)
{
	CImage * pImage = GetBlankImage(1.0, false);
	GrabScreen(*pImage);
	pImage->Save(strFileName);
	delete pImage;
	return true;
}

int CDrawableWidget::OnCreate(LPCREATESTRUCT lpCreateStruct)
{
	int nResult = CWnd::OnCreate(lpCreateStruct);
	
	// InitOglStuff();
	
	return nResult;
}

GLfloat lightAmbient[] = { 0.2f, 0.2f, 0.2f, 1.0f };
GLfloat lightDiffuse[] = { 0.7f, 0.7f, 0.7f, 1.0f };
GLfloat lightSpecular[] = { 0.9f, 0.9f, 0.9f };
GLfloat materialColor[] = { 0.8f, 0.0f, 0.0f };
// GLfloat lightpos[] = { -80.0f, 120.0f, 100.0f, 0.0f };
GLfloat lightpos[] = { 150.0f, 20.0f, 100.0f, 1.0f };

void CDrawableWidget::DrawGlScene(void) 
{ 
	glEnable(GL_DEPTH_TEST);
	glDepthFunc(GL_LEQUAL);
	glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
	glMatrixMode( GL_MODELVIEW );

	glMatrixScope globalScope;

	glLoadIdentity();




	for(int i = 0; i < m_vecCustomDraw.size(); ++i)
	{
		m_vecCustomDraw[i]->Draw();
	}


	if(m_vecCustomDraw.size() < 1)
	{
		
		glMatrixScope Scope1;
		
		// glLoadIdentity();
		// glClear( GL_ALL_ATTRIB_BITS | GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT ); 

	 
		// glLightfv(GL_LIGHT0,GL_POSITION, lightpos);
		glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmbient);
		glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDiffuse);
		glLightfv(GL_LIGHT0, GL_SPECULAR, lightSpecular);
		glEnable(GL_LIGHTING);
		glEnable(GL_LIGHT0);
		glMaterialfv(GL_FRONT, GL_SPECULAR,lightSpecular);
		glMaterialfv(GL_FRONT, GL_AMBIENT_AND_DIFFUSE, materialColor);
		glMateriali(GL_FRONT, GL_SHININESS,128); 

		// glDisable(GL_DEPTH_TEST);

		// TRACE("Supposedly cleared\n");
	     
		// glLoadIdentity();

		static int nColorCnt = 0;
		++nColorCnt;




		// glColor3d(1.0-0.5*sin(0.1*nColorCnt), 0.0, 1.0-0.5*cos(0.1*nColorCnt)); // Blue
		glColor3d(0.1, 0.0, 1.0);
		
		{
			glMatrixScope Scope2;
			// glLoadIdentity();
			
			glTranslated(-100.0, -100.0, 0.0);
			for(int nXSquare = 0; nXSquare < 20; ++nXSquare)
			{
				{
					glMatrixScope Scope3;
					for(int nYSquare = 0; nYSquare < 20; ++nYSquare)
					{
						glBegin(GL_QUADS);
						glVertex3d(4.0, 4.0, 0.0);
						glVertex3d(-4.0, 4.0, 0.0);
						glVertex3d(-4.0, -4.0, 0.0);
						glVertex3d(4.0, -4.0, 0.0);

						glEnd();


						glBegin(GL_QUADS);
						glVertex3d(-4.0, 4.0, -1.0);
						glVertex3d(4.0, 4.0, -1.0);
						glVertex3d(4.0, -4.0, -1.0);
						glVertex3d(-4.0, -4.0, -1.0);

						glEnd();
						glTranslated(0.0, 10.0, 0.0);
					}
				}				
				glTranslated(10.0, 0.0, 0.0);
			}

		}



		
	 
	}
 
	BOOL bRet = SwapBuffers(SafeGrabDC()->GetSafeHdc());
	if(bRet)
	{
		// TRACE("SwapBuffers returned True\n");
	}
	else
	{
		TRACE("SwapBuffers returned False\n");
	}

	SafeReleaseDC();
}

void CDrawableWidget::OnMouseMove(UINT nFlags, CPoint point)
{
	// TRACE("There\n");
	RECT wRect;
	// m_bMouseStarted
	GetWindowRect(&wRect);
	
	double lfScaleX = wRect.right - wRect.left;
	double lfScaleY = wRect.bottom - wRect.top;

	if(lfScaleX < 0.001 || lfScaleY < 0.001)
	{
		return;
	}

	lfScaleX = 1.0/lfScaleX; lfScaleY = 1.0/lfScaleY;

	double lfMouseX = (point.x)*lfScaleX;
	double lfMouseY = (point.y)*lfScaleY;

	//TRACE("Mouse move: lfMouseX=%lf\t lfMouseY=%lf\t\n", lfMouseX, lfMouseY);
	//TRACE("x is %d, y is %d, right is %d, left is %d, top is %d, bottom is %d\n",
	//	point.x, point.y, wRect.right, wRect.left, wRect.top, wRect.bottom);

	if(lfMouseX >= 0.0 && lfMouseX <= 1.0 && 
		lfMouseY >= 0.0 && lfMouseY <= 1.0)
	{
		m_bMouseStarted = true;
		m_arrLfMousePos[0] = lfMouseX;
		m_arrLfMousePos[1] = lfMouseY;
		m_pointMouseAsInts.SetPoint(point.x, point.y);
		m_nFlags = nFlags;

		if(m_pCustomOnMouse)
		{
			m_pCustomOnMouse->OnMouseMove(nFlags, lfMouseX, lfMouseY);
		}
	}

	
	
	OnPaint();
}


void CDrawableWidget::DrawScreen()
{
	
	HGLRC glRc = InitOglStuff();
	
	
	DrawGlScene();
	wglMakeCurrent(NULL, NULL);
	wglDeleteContext(glRc);

	if(m_pLinkedWidget)
	{
		glRc = m_pLinkedWidget->InitOglStuff();
		m_pLinkedWidget->DrawGlScene();
		wglMakeCurrent(NULL, NULL);
		wglDeleteContext(glRc);
	}
	
	// 	TRACE("Draw stuff\n");

	
}

void CDrawableWidget::OnPaint()
{
	// CButton::OnPaint();
	BeginPaint(&m_ps);
	DrawScreen();
	EndPaint(&m_ps);
}




